#include "../../core/graphic/texture.h"
#include <GLES/gl.h>

struct ANDROID_TEXTURE_DESC
{
	int w;
	int h;
	KFormat format;
	GLint glFormat;
	GLuint textureId;
	BYTE* pTempData;

	ANDROID_TEXTURE_DESC() {textureId = 0; pTempData = NULL;}
};


KTexture::KTexture()
{
	mTextureData = new ANDROID_TEXTURE_DESC;
}

KTexture::~KTexture()
{
	Reset();	

	ANDROID_TEXTURE_DESC* pTex = (ANDROID_TEXTURE_DESC*)mTextureData;
	delete pTex;
}

// Implementation dependent methods
bool KTexture::CreateFromFile(const char* fileName, bool forceGray)
{
	Reset();

	KBitmap::CreationDesc cdesc;
	cdesc.forceGray = forceGray;
	bool result = KBitmap::LoadFromFile(fileName, this, cdesc);

	return result;
}

void KTexture::Reset()
{
	ANDROID_TEXTURE_DESC* pTex = (ANDROID_TEXTURE_DESC*)mTextureData;
	if (pTex->textureId != 0) {
		glDeleteTextures( 1, &pTex->textureId );
	}
}

bool KTexture::CreateFromScratch(int w, int h, KFormat format)
{
	return Allocate(w, h, format);
}

bool KTexture::UpdateRect(int sx, int sy, int w, int h, void* pData)
{
	ANDROID_TEXTURE_DESC* pTex = (ANDROID_TEXTURE_DESC*)mTextureData;
	
	glBindTexture(GL_TEXTURE_2D, pTex->textureId);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexSubImage2D(
		GL_TEXTURE_2D, 0,
		sx, sy,
		w, h,
		pTex->glFormat,
		GL_UNSIGNED_BYTE,
		pData);
	
	return true;
}



bool KTexture::Bind()
{
	ANDROID_TEXTURE_DESC* pTex = (ANDROID_TEXTURE_DESC*)mTextureData;
	glBindTexture(GL_TEXTURE_2D, pTex->textureId);
	return true;
}

bool KTexture::BeginEdit()
{
	ANDROID_TEXTURE_DESC* pTex = (ANDROID_TEXTURE_DESC*)mTextureData;
	pTex->pTempData = (BYTE*)malloc(pTex->w * pTex->h * mBpp);
	return true;
}

void KTexture::EndEdit()
{
	ANDROID_TEXTURE_DESC* pTex = (ANDROID_TEXTURE_DESC*)mTextureData;
	glBindTexture(GL_TEXTURE_2D, pTex->textureId);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexSubImage2D(
		GL_TEXTURE_2D, 0,
		0, 0,
		pTex->w, pTex->h,
		pTex->glFormat,
		GL_UNSIGNED_BYTE,
		pTex->pTempData);

	free(pTex->pTempData);
	pTex->pTempData = NULL;
}

void* KTexture::GetRowPtr(int row)
{
	ANDROID_TEXTURE_DESC* pTex = (ANDROID_TEXTURE_DESC*)mTextureData;
	if (pTex->pTempData) {
		BYTE* pDataInByte = (BYTE*)pTex->pTempData;
		return &pDataInByte[row * pTex->w * mBpp];
	}
	else
		return NULL;
}

bool KTexture::Allocate(int w, int h, KFormat format)
{
	ANDROID_TEXTURE_DESC* pTex = (ANDROID_TEXTURE_DESC*)mTextureData;
	glGenTextures(1, &pTex->textureId);
	glBindTexture(GL_TEXTURE_2D, pTex->textureId);
	pTex->glFormat = (format == eRGBA_8888 ? GL_RGBA : GL_ALPHA);
	if (format == eRGBA_8888)
		mBpp = 4;
	else if (format == eRGBA_L8)
		mBpp = 1;
	std::vector<BYTE> tempData(mBpp * w * h);
	for (unsigned int i = 0; i < tempData.size(); ++i)
		tempData[i] = 0x00;
	glTexImage2D(
		GL_TEXTURE_2D, 0,
		pTex->glFormat,
		w, h,
		0,
		pTex->glFormat,
		GL_UNSIGNED_BYTE,
		&tempData[0]  // Do not specify data at the time
		);
	glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
	
	// when texture area is small, bilinear filter the closest mipmap
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
	// when texture area is large, bilinear filter the original
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

	// the texture wraps over at the edges (repeat)
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );

	mWidth = w;
	mHeight = h;
	pTex->w = w;
	pTex->h = h;
	pTex->format = format;
	
	return true;
}
